package com.littocats.filesystem;

import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.bridge.WritableNativeArray;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.Nullable;

/**
 * Created by Dragon-Li on 5/9/17.
 */

public class FSModule extends ReactContextBaseJavaModule {

    static char HexDetable[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0,0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,11,12,13,14,15};
    static char HexTable[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
    public FSModule(ReactApplicationContext reactContext) {
        super(reactContext);
    }

    @Override
    public String getName() {
        return "LittoFileSystem";
    }

    @Nullable
    @Override
    public Map<String, Object> getConstants() {
        Map<String, Object> consts = new HashMap<>();
        consts.put("HOME", getReactApplicationContext().getExternalFilesDir(null).getAbsolutePath());
        consts.put("TEMP", getReactApplicationContext().getExternalCacheDir().getAbsolutePath());
        return consts;
    }


//NativeFS.read
//NativeFS.write
//NativeFS.stat
//NativeFS.isFile
//NativeFS.mkdir
//NativeFS.touch
//NativeFS.exists
//NativeFS.remove

    @ReactMethod
    public void read(String path, int offset, int length, Promise promise) {
        try {
            RandomAccessFile file = new RandomAccessFile(path, "r");
            file.seek(offset);
            byte[] bytes = new byte[length];
            length = file.read(bytes, 0, length);
            file.close();
            StringBuffer sb = new StringBuffer();
            int index = 0;
            while (index < length) {
                int b = bytes[index++];
                sb.append(HexTable[(b >> 4) & 0xf]);
                sb.append(HexTable[b & 0xf]);
            }
            promise.resolve(sb.toString());
        } catch (FileNotFoundException e) {
            promise.reject(e);
        } catch (IOException e) {
            promise.reject(e);
        }
    }

    @ReactMethod
    public void write(String path, int offset, String hex, Promise promise) {
        try {
            byte[] bytes = new byte[hex.length()/2];
            int index = 0;
            while (index < hex.length()) {
                char h = HexDetable[hex.charAt(index++)];
                char l = HexDetable[hex.charAt(index++)];
                bytes[index/2-1] = (byte) (((h << 4) & 0xf0) | (l & 0xf));
            }

            RandomAccessFile file = new RandomAccessFile(path, "rw");
            file.seek(offset);
            file.write(bytes, 0, hex.length()/2);
            file.close();

            promise.resolve(hex.length()/2);
        } catch (FileNotFoundException e) {
            promise.reject(e);
        } catch (IOException e) {
            promise.reject(e);
        }
    }

    @ReactMethod
    public void stat(String path, Promise promise) {
        File file = new File(path);
        WritableMap wm = new WritableNativeMap();
        wm.putString("type", file.isFile() ? "file" : file.isDirectory() ? "directory" : "unknown");
        wm.putDouble("size", file.length());
        wm.putDouble("mtime", file.lastModified());
        promise.resolve(wm);
    }

    @ReactMethod
    public void isFile(String path, Promise promise) {
        boolean isFile = new File(path).isFile();
        promise.resolve(isFile);
    }

    @ReactMethod
    public void mkdir(String path, Boolean autoCreateParent, Promise promise) {
        File file = new File(path);
        if (autoCreateParent) {
            promise.resolve(file.mkdirs());
        }else {
            promise.resolve(file.mkdir());
        }
    }

    @ReactMethod
    public void touch(String path, Boolean autoCreateParent, Promise promise) {
        File file = new File(path);
        if (autoCreateParent) {
            file.getParentFile().mkdirs();
        }
        try {
            promise.resolve(file.createNewFile());
        } catch (IOException e) {
            promise.reject(e);
        }
    }

    @ReactMethod
    public void exists(String path, Promise promise) {
        boolean exists = new File(path).exists();
        promise.resolve(exists);
    }

    @ReactMethod
    public void remove(String path, Promise promise) {
        remove(new File(path));
        promise.resolve(null);
    }

    @ReactMethod
    public void readDir(String path, boolean recusive, Promise promise) {
        WritableArray array = new WritableNativeArray();
        readDir(new File(path), array, recusive);
        promise.resolve(array);
    }

    private void readDir(File file, WritableArray array, boolean recusive) {
        if (!file.isDirectory()) return;
        File items[] = file.listFiles();
        for (File item : items) {
            array.pushString(item.getPath());
            if (recusive) readDir(item, array, recusive);
        }
    }

    private void remove(File file) {
        if (file.isFile()) {
            file.deleteOnExit();
        }else if (file.isDirectory()){
            File files[] = file.listFiles();
            for (File f: files) {
                remove(f);
            }
        }
    }
}
